diff options
| author | joonhoekim <26rote@gmail.com> | 2025-11-25 22:04:56 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-11-25 22:04:56 +0900 |
| commit | 2b59582194fc5c23140f52c42c793c324856a35e (patch) | |
| tree | 0db8ef0e913b3a44dfd6e3e20fe92b8e4984aeba /app/[lng] | |
| parent | 835df8ddc115ffa74414db2a4fab7efc0d0056a9 (diff) | |
(김준회) 벤더풀&AVL 구매 추가요청사항 반영
Diffstat (limited to 'app/[lng]')
| -rw-r--r-- | app/[lng]/evcp/(evcp)/(procurement)/avl/[id]/page.tsx | 17 | ||||
| -rw-r--r-- | app/[lng]/evcp/(evcp)/(procurement)/vendor-pool/page.tsx | 193 |
2 files changed, 55 insertions, 155 deletions
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/avl/[id]/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/avl/[id]/page.tsx index b065919f..c0230013 100644 --- a/app/[lng]/evcp/(evcp)/(procurement)/avl/[id]/page.tsx +++ b/app/[lng]/evcp/(evcp)/(procurement)/avl/[id]/page.tsx @@ -4,9 +4,9 @@ import { notFound } from "next/navigation" import { getValidFilters } from "@/lib/data-table" import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" -import { getAvlLists, getAvlDetail } from "@/lib/avl/service" +import { getAllAvlDetail } from "@/lib/avl/service" import { avlDetailSearchParamsCache } from "@/lib/avl/validations" -import { AvlDetailTable } from "@/lib/avl/table/avl-detail-table" +import { AvlDetailVirtualTable } from "@/lib/avl/table/avl-detail-virtual-table" import { getAvlListById } from "@/lib/avl/service" import { getAllProjectInfoByProjectCode as getProjectInfoFromBiddingProjects } from "@/lib/bidding-projects/service" import { getAllProjectInfoByProjectCode as getProjectInfoFromProjects } from "@/lib/projects/service" @@ -36,21 +36,17 @@ export default async function AvlDetailPage(props: AvlDetailPageProps) { } // 프로젝트 테이블 먼저 - let projectInfo = await getProjectInfoFromProjects(avlListInfo.projectCode || '') + let projectInfo: any = await getProjectInfoFromProjects(avlListInfo.projectCode || '') // 없으면 견적프로젝트 테이블 조회 if (!projectInfo) { projectInfo = await getProjectInfoFromBiddingProjects(avlListInfo.projectCode || '') } // 배열로 오니 첫번째것만 - projectInfo = projectInfo[0] + projectInfo = projectInfo[0] as any const promises = Promise.all([ - getAvlDetail({ - ...search, - filters: validFilters, - avlListId: numericId, - }), + getAllAvlDetail(numericId), ]) return ( @@ -108,9 +104,8 @@ function AvlDetailTableWrapper({ const shipOwnerName = avlListInfo.shipOwnerName || undefined return ( - <AvlDetailTable + <AvlDetailVirtualTable data={data} - pageCount={pageCount} avlListId={avlListId} avlType={avlType} projectInfo={projectInfo} diff --git a/app/[lng]/evcp/(evcp)/(procurement)/vendor-pool/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/vendor-pool/page.tsx index 7426e069..f18716a3 100644 --- a/app/[lng]/evcp/(evcp)/(procurement)/vendor-pool/page.tsx +++ b/app/[lng]/evcp/(evcp)/(procurement)/vendor-pool/page.tsx @@ -1,25 +1,52 @@ "use client" import * as React from "react" -import { type SearchParams } from "@/types/table" - -import { getValidFilters } from "@/lib/data-table" -import { Skeleton } from "@/components/ui/skeleton" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" import { Shell } from "@/components/shell" -import { getVendorPools } from "@/lib/vendor-pool/service" -import { vendorPoolSearchParamsCache } from "@/lib/vendor-pool/validations" -import { VendorPoolTable } from "@/lib/vendor-pool/table/vendor-pool-table" import { InformationButton } from "@/components/information/information-button" -import { useSearchParams } from "next/navigation" +import { VendorPoolVirtualTable } from "@/lib/vendor-pool/table/vendor-pool-virtual-table" +import { Skeleton } from "@/components/ui/skeleton" +import type { VendorPoolItem } from "@/lib/vendor-pool/table/vendor-pool-table-columns" +import { toast } from "sonner" -interface VendorPoolPageProps { - searchParams: Promise<SearchParams> -} +export default function VendorPoolPage() { + const [data, setData] = React.useState<VendorPoolItem[]>([]) + const [isLoading, setIsLoading] = React.useState(true) + + // 전체 데이터 로드 + const loadData = React.useCallback(async () => { + setIsLoading(true) + try { + const response = await fetch('/api/vendor-pool/all') + if (!response.ok) { + throw new Error('Failed to fetch data') + } + const result = await response.json() + setData(result) + } catch (error) { + console.error('Failed to load vendor pool data:', error) + toast.error('데이터를 불러오는데 실패했습니다.') + } finally { + setIsLoading(false) + } + }, []) + + // 초기 로드 + React.useEffect(() => { + loadData() + }, []) // ✅ 빈 배열로 변경 - 마운트시에만 실행 + + // 새로고침 핸들러 - useRef를 사용하여 안정적인 참조 유지 + const loadDataRef = React.useRef(loadData) + React.useEffect(() => { + loadDataRef.current = loadData + }, [loadData]) + + const handleRefresh = React.useCallback(() => { + loadDataRef.current() + }, []) // ✅ 빈 배열로 변경 - 함수 재생성 방지 -export default function VendorPoolPage({ searchParams }: VendorPoolPageProps) { return ( - <Shell className="gap-2"> + <Shell variant="fullscreen" className="gap-2 h-[calc(100vh-150px)]"> <div className="flex items-center justify-between space-y-2"> <div className="flex items-center justify-between space-y-2"> <div> @@ -33,136 +60,14 @@ export default function VendorPoolPage({ searchParams }: VendorPoolPageProps) { </div> </div> - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}> - </React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={30} - searchableColumnCount={1} - filterableColumnCount={5} - cellWidths={[ - "50px", "60px", "100px", "80px", "120px", "120px", "120px", "120px", - "120px", "100px", "140px", "140px", "130px", "120px", "80px", "100px", - "120px", "80px", "100px", "120px", "100px", "110px", "140px", "130px", - "60px", "100px", "80px", "120px", "80px", "80px" - ]} - shrinkZero - /> - } - > - <VendorPoolTableWrapperClient searchParamsPromise={searchParams} /> - </React.Suspense> + {isLoading ? ( + <div className="space-y-4 flex-1 flex flex-col"> + <Skeleton className="h-10 w-full" /> + <Skeleton className="h-full w-full flex-1" /> + </div> + ) : ( + <VendorPoolVirtualTable data={data} onRefresh={handleRefresh} /> + )} </Shell> ) } - -// 클라이언트 컴포넌트: 필터 변경을 감시하여 데이터 재조회 -function VendorPoolTableWrapperClient({ searchParamsPromise }: { searchParamsPromise: Promise<SearchParams> }) { - const searchParams = useSearchParams() - const [initialData, setInitialData] = React.useState<{ data: any[], pageCount: number } | null>(null) - const [data, setData] = React.useState<any[]>([]) - const [pageCount, setPageCount] = React.useState(0) - const [isLoading, setIsLoading] = React.useState(false) - - // 초기 데이터 로딩 - React.useEffect(() => { - const loadInitialData = async () => { - try { - const searchParamsData = await searchParamsPromise - const search = vendorPoolSearchParamsCache.parse(searchParamsData) - const validFilters = getValidFilters(search.filters) - - const result = await getVendorPools({ - ...search, - filters: validFilters, - }) - - setInitialData(result) - setData(result.data) - setPageCount(result.pageCount) - } catch (error) { - console.error('Failed to load initial data:', error) - } - } - loadInitialData() - }, [searchParamsPromise]) - - // 필터 상태 변경 감시 및 데이터 재조회 - React.useEffect(() => { - if (!initialData) return // 초기 데이터가 로드되기 전까지는 실행하지 않음 - - const refreshData = async () => { - setIsLoading(true) - try { - const currentParams = Object.fromEntries(searchParams.entries()) - const search = vendorPoolSearchParamsCache.parse(currentParams) - const validFilters = getValidFilters(search.filters) - - const result = await getVendorPools({ - ...search, - filters: validFilters, - }) - - setData(result.data) - setPageCount(result.pageCount) - } catch (error) { - console.error('Failed to refresh vendor pool data:', error) - } finally { - setIsLoading(false) - } - } - - // 필터 파라미터가 변경될 때마다 데이터 재조회 - const currentFilters = searchParams.get('filters') - const currentJoinOperator = searchParams.get('joinOperator') - const currentSearch = searchParams.get('search') - const currentPage = searchParams.get('page') - const currentPerPage = searchParams.get('perPage') - const currentSort = searchParams.get('sort') - - // 필터 관련 파라미터가 변경되면 재조회 - if (currentFilters !== '[]' || currentJoinOperator || currentSearch || currentPage || currentPerPage || currentSort) { - refreshData() - } else { - // 필터가 초기 상태면 초기 데이터로 복원 - setData(initialData.data) - setPageCount(initialData.pageCount) - } - }, [searchParams, initialData]) - - const handleRefresh = React.useCallback(async () => { - if (!initialData) return - - setIsLoading(true) - try { - const currentParams = Object.fromEntries(searchParams.entries()) - const search = vendorPoolSearchParamsCache.parse(currentParams) - const validFilters = getValidFilters(search.filters) - - const result = await getVendorPools({ - ...search, - filters: validFilters, - }) - - setData(result.data) - setPageCount(result.pageCount) - } catch (error) { - console.error('Failed to refresh vendor pool data:', error) - } finally { - setIsLoading(false) - } - }, [searchParams, initialData]) - - if (!initialData) { - return null // 초기 데이터 로딩 중 - } - - return ( - <VendorPoolTable - data={data} - pageCount={pageCount} - onRefresh={handleRefresh} - /> - ) -} |
